package com.apes.fn.scm.crossCompany.transfer.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.apes.fn.scm.crossCompany.transfer.model.*;
import com.apes.fn.scm.crossCompany.transfer.repository.BatchDumpItemRepository;
import com.apes.fn.scm.crossCompany.transfer.repository.BatchDumpRepository;
import com.apes.fn.scm.crossCompany.transfer.repository.TransferProductRepository;
import com.apes.fn.server.productRequire.ProductRequireItemRepository;
import com.apes.framework.api.DomainService;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.jpa.spec.model.Filter;
import com.apes.framework.jpa.spec.model.Group;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.plugin.outbox.*;
import com.apes.framework.plugin.outbox.annotation.OutboxSubscriber;
import com.apes.framework.util.MapUtil;
import com.apes.scm.account.account.service.ProductAccountService;
import com.apes.scm.contract.model.Contract;
import com.apes.scm.contract.repository.ContractRepository;
import com.apes.scm.masterdata.enumeration.repository.EnumerationRepository;
import com.apes.scm.masterdata.frame.service.StoreService;
import com.apes.scm.masterdata.party.model.PartyRole;
import com.apes.scm.masterdata.party.repository.PartyRoleRepository;
import com.apes.scm.masterdata.product.model.Product;
import com.apes.scm.masterdata.product.model.ProductUom;
import com.apes.scm.masterdata.product.repository.ProductRepository;
import com.apes.scm.masterdata.product.repository.ProductUomRepository;
import com.apes.scm.masterdata.stock.model.Location;
import com.apes.scm.masterdata.stock.repository.LocationRepository;
import com.apes.scm.stock.transfer.model.Transfer;
import com.apes.scm.stock.transfer.model.TransferItem;
import com.apes.scm.voucher.service.builder.CrossTransferVoucherBuilder;
import org.apache.commons.collections.map.HashedMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toCollection;

/**
 */
@Service("batchDumpService")
public class BatchDumpService extends DomainService {

    @Autowired
    private BatchDumpRepository batchDumpRepository;
    @Autowired
    private BatchDumpItemRepository batchDumpItemRepository;
    @Autowired
    private CrossTransferVoucherBuilder crossTransferVoucherBuilder;
    @Autowired
    private ContractRepository contractRepository;
    @Autowired
    private PartyRoleRepository partyRoleRepository;
    @Autowired
    private EnumerationRepository enumerationRepository;
    @Autowired
    private OutboxEventService outboxEventService;
    @Autowired
    private ProductRequireItemRepository queryStockRepository;
    @Autowired
    private ProductUomRepository productUomRepository;
    @Autowired
    private ProductRepository productRepository;
    @Autowired
    private LocationRepository locationRepository;
    @Autowired
    private CrossCompanyTransferNewService crossCompanyTransferNewService;

    // 创建
    public BatchDump save(SimpleRequest request) {
        BatchDump batchDump = request.getO(BatchDump.class);
        batchDump.setState("sent");
        batchDump.setCreateUid(request.getPerson());
        return batchDumpRepository.saveAndFlush(batchDump);
    }

    // 修改
    public BatchDump update(SimpleRequest request) {
        BatchDump batchDump = request.getO(BatchDump.class);
        return batchDumpRepository.saveAndFlush(batchDump);
    }

    // 删除
    public String delete(SimpleRequest request) {
        BatchDump batchDump = request.getO(BatchDump.class);
        batchDump.setState("delete");
        batchDump.setDeleteUid(request.getPerson());
        batchDump.setDeleteDate(new Date());
        batchDumpRepository.save(batchDump);
        return "删除成功";
    }

    // 审核
    public BatchDump approve(SimpleRequest request) {
        List<OutboxEvent> events = SpringManager.getBean(OutboxEventRepository.class).findAll(" SELECT id FROM OUTBOX_EVENT WHERE AGGREGATE_ID LIKE 'dumpTransfer:%' OR AGGREGATE_ID LIKE 'crossDumpTransfer:%' ");
        if (events.size() > 5) {
            throw new RuntimeException("还有单据未完成，数据生成中，请稍后再试！");
        }
        BatchDump batchDump = request.getO(BatchDump.class);
        batchDump.setApproveUid(request.getPerson());
        batchDump.setDateApprove(new Date());
        batchDump.setState("approve");

        // 审核完成之后，发送事务发件箱
        JSONObject jsonObject = new JSONObject(1);
        jsonObject.put("id", batchDump.getId());
        requestOutboxAppend(batchDump.getId(), "batchDump.approve.createTransfer", jsonObject.toJSONString());

        return batchDumpRepository.saveAndFlush(batchDump);
    }

    @OutboxSubscriber(id = "batchDump.approve.createTransfer", name = "批量补货生成转储单")
    public void createBatchDumpTransferOutBox(OutboxPayloadWrapper wrapper){
        JSONObject tempJson = JSON.parseObject(wrapper.getPayload());
        String id = tempJson.getString("id");

        BatchDump dump = batchDumpRepository.findOne(id);
        dump.getBatchDumpItems().sort(Comparator.comparing(item -> item.getProduct().getClassify().getId()));

        createBatchDumpTransfer(dump);
    }

    public void testCreateBatchDumpTransfer(SimpleRequest request){
        String id = request.get("id");
        BatchDump batchDump = batchDumpRepository.findOne(id);
        createBatchDumpTransfer(batchDump);
    }

    private void createBatchDumpTransfer(BatchDump batchDump){
        // 根据仓库分组
        batchDump.getBatchDumpItems().stream().
                collect(Collectors.groupingBy(BatchDumpItem::getReceiptLocation))
                .forEach((receiptLocation, batchDumpItems) -> {
                    // 判断是否为跨公司
                    if (batchDump.getShipLocation().getCompany().getId().equals(receiptLocation.getCompany().getId())) {
                        // 非跨公司
                        dumpTransfer(batchDump, batchDumpItems, 0);
                    } else {
                        // 跨公司
                        crossDumpTransfer(batchDump, batchDumpItems, 0);
                    }
                });
    }

    /**
     * 审核完成之后，通过事务发件箱生成转储单
     *
     * @param batchDump      BatchDump
     * @param batchDumpItems batchDumpItems
     */
    private void dumpTransfer(BatchDump batchDump, List<BatchDumpItem> batchDumpItems, int cycles) {
        if (judgeFCKBJError(batchDumpItems.get(0).getReceiptLocation(), batchDump.getShipLocation(), batchDump.getId())) return;

        // 拼接 Transfer 数据
        Transfer transfer = appendTransfer(batchDump, batchDumpItems.get(0));
        addTransferItem(transfer, batchDumpItems);
        if (transfer.getTransferItems().isEmpty()) return;

        Map transferMap = new HashedMap();
        transferMap.put("uniqueKey_", transfer.getSource() + transfer.getReceiptLocation().getId() + cycles); // 防重用
        transferMap.put("transferNew",  MapUtil.mapped(transfer));
        transferMap.put("source", batchDump.getId());

        // 把数据发到事务发件箱
//        String aggregateId = "dumpTransfer:" + batchDump.getId() + "," + transfer.getReceiptLocation().getId() + cycles;
        String aggregateId = "dumpTransfer: " + batchDump.getId();
        requestOutboxAppend(aggregateId, "local:batchDumpService.saveTransfer", JSONObject.toJSONString(transferMap));

        List<BatchDumpItem> tackApartBatchDumps = batchDumpItems.size() > 100?takeApartBatchDumapItem(batchDumpItems):null;
        if (tackApartBatchDumps != null && tackApartBatchDumps.size() != 0) this.dumpTransfer(batchDump, tackApartBatchDumps, cycles + 1);
    }

    // 转储判断仓库是否有效
    private boolean judgeFCKBJError(Location receiptLocation, Location shipLocation, String batchDumpId) {
        try {
            SpringManager.getBean(StoreService.class).judgeFCKBJ(receiptLocation);
            SpringManager.getBean(StoreService.class).judgeFCKBJ(shipLocation);
        } catch (Exception e) {
            this.updateErrorReason(MapUtil.mapper("NOTE", e.getMessage(), "BATCH_DUMP_ID", batchDumpId, "RECEIPT_LOCATION_ID", receiptLocation.getId()));
            return true;
        }
        return false;
    }

    // 拆明细 - 每一百成一单, 返回剩余明细
    private List<BatchDumpItem> takeApartBatchDumapItem(List<BatchDumpItem> oldBatchDumps) {
        // 返回剩余单数
        if (oldBatchDumps.size() <= 100) return null;
        return oldBatchDumps.subList(99, oldBatchDumps.size() - 1);
    }

    // 转储明细数据拼接
    private void addTransferItem(Transfer transfer, List<BatchDumpItem> batchDumpItems) {

        Set<TransferItem> transferItems = new HashSet<>();
        List<BatchDumpItem> newBatchDumpItems = batchDumpItems.size() > 100?batchDumpItems.subList(0, batchDumpItems.size() - 1):batchDumpItems;
        newBatchDumpItems.forEach(batchDumpItem -> {
            List<Map> stocks = qTransferStock(transfer.getOriginLocation().getId(), batchDumpItem.getProduct().getId());
            if (stocks.size() == 0) {
                notedErrorMsg(batchDumpItem.getId(), "库存不足，没有找到对应的库存信息");
                return;
            }

            // 查询到有库存，但库存不足
            double usableQty = Double.parseDouble(stocks.get(0).get("usableQty").toString());
            if (usableQty < batchDumpItem.getProductSum()) {
                notedErrorMsg(batchDumpItem.getId(), "库存不足");
                return;
            }

            // 满足条件，组装数据
            TransferItem transferItem = new TransferItem();
            transferItem.setProduct(batchDumpItem.getProduct());
            transferItem.setUom(batchDumpItem.getUom());
            transferItem.setTransferQty(batchDumpItem.getProductSum());
            transferItem.setNote(batchDumpItem.getNote());
            transferItem.setTransfer(transfer);
            transferItems.add(transferItem);
        });

        // 添加明细
        transfer.setTransferItems(transferItems);
    }


    /**
     * 审核完成之后，通过事务发件箱生成跨公司调拨单
     *
     * @param batchDump      BatchDump
     * @param batchDumpItems batchDumpItems
     */
    private void crossDumpTransfer(BatchDump batchDump, List<BatchDumpItem> batchDumpItems, int cycles) {
        // 整理调拨范围数据
        Map transferProductData = qryTransferProduct(batchDump.getShipLocation(), batchDumpItems.get(0).getReceiptLocation(), batchDump.getId());

        // 跨公司转储
        CrossCompanyTransferNew crossNew = appendCross(batchDump, batchDumpItems.get(0));

        if (transferProductData.isEmpty()) return;
        // 获取明细
        addCrossItem(crossNew, batchDumpItems, transferProductData);
        if (crossNew.getCrossCompanyTransferItems().isEmpty()) return;

        Map resultEnd = this.otherData(transferProductData, crossNew, cycles);

        // 把数据发到事务发件箱
//        String key = "batchDump-cross-id:" + batchDump.getId() + "," + crossNew.getReceiptLocation().getId() + cycles; // 勿随意更改格式，有用到
        String key = "crossDumpTransfer:" + batchDump.getId();
        requestOutboxAppend(key, "local:batchDumpService.saveCrossNew", JSONObject.toJSONString(resultEnd));

        List<BatchDumpItem> tackApartBatchDumps = batchDumpItems.size() > 100?takeApartBatchDumapItem(batchDumpItems):null;
        if (tackApartBatchDumps != null && tackApartBatchDumps.size() != 0) this.crossDumpTransfer(batchDump, tackApartBatchDumps, cycles + 1);
    }

    // 跨公司调拨 - 额外数据
    private Map otherData(Map transferProductData, CrossCompanyTransferNew crossNew, int cycles) {
        Map resultEnd = new HashedMap();
        resultEnd.put("checkContract", transferProductData.get("checkContract"));
        resultEnd.put("source", crossNew.getSource());
        resultEnd.put("flag", transferProductData.get("flag"));
        resultEnd.put("suppliers", MapUtil.mapped(transferProductData.get("suppliers")));
        resultEnd.put("crossNew", MapUtil.mapped(crossNew));
        resultEnd.put("uniqueKey_", crossNew.getSource() + crossNew.getReceiptLocation().getId() + cycles); // 防重用
        return resultEnd;
    }

    // 跨公司明细数据拼接
    private void addCrossItem(CrossCompanyTransferNew crossNew, List<BatchDumpItem> batchDumpItems, Map transferProductM) {
        Set<CrossCompanyTransferItemNew> crossNewItems = new HashSet<>();
        List<PartyRole> suppliers = (List<PartyRole>) transferProductM.get("suppliers");
        final boolean allSuppliers = (boolean) transferProductM.get("flag");
        if (suppliers.size() == 0 && !allSuppliers) return;

        List<BatchDumpItem> newBatchDumpItems = batchDumpItems.size() > 100?batchDumpItems.subList(0, batchDumpItems.size() - 1):batchDumpItems;
        newBatchDumpItems.forEach(batchDumpItem -> {
            // 查询无库存
            Map allStocks = new HashedMap();
            if (allSuppliers) {
                allStocks = queryStockRepository.availableStockAllSupplier(crossNew.getShipLocation().getId(), batchDumpItem.getProduct().getId());
            } else {
                allStocks = queryStockRepository.validStock(crossNew.getShipLocation().getId(), batchDumpItem.getProduct().getId(), suppliers);
            }

            // 没有库存的情况
            if (allStocks.size() == 0) {
                notedErrorMsg(batchDumpItem.getId(), "请检查调拨范围 或 库存是否充足");
                return;
            }

            // 有库存时的判断
            double newUsableQty = 0;
            if (!allStocks.get("PRODUCT_UOM_ID").toString().equals(batchDumpItem.getUom().getId())) {
                // 当计量单位不一致的时候，这里做一次转换
                ProductUom stockUom = productUomRepository.findAll(JpaDsl.toCriteriaByEq("uomCategory.id", batchDumpItem.getProduct().getId(), "factor", 1)).get(0);
                newUsableQty = Math.floor(stockUom.computeQty(Double.parseDouble(allStocks.get("USABLE_QTY").toString()), batchDumpItem.getUom(), false));
            }

            // 检查是否库存不足
            if (newUsableQty == 0 || batchDumpItem.getProductSum() > newUsableQty) {
                notedErrorMsg(batchDumpItem.getId(), "库存不足");
                return;
            }

            CrossCompanyTransferItemNew crossNewItem = new CrossCompanyTransferItemNew();
            crossNewItem.setCrossCompanyTransfer(crossNew);
            crossNewItem.setProduct(batchDumpItem.getProduct());
            crossNewItem.setUom(batchDumpItem.getUom());
            crossNewItem.setProductSumQty(newUsableQty);
            crossNewItem.setProductSum(batchDumpItem.getProductSum() > newUsableQty?newUsableQty:batchDumpItem.getProductSum());
            crossNewItem.setNote(batchDumpItem.getNote());
            crossNewItems.add(crossNewItem);
        });

        // 添加明细
        crossNew.setCrossCompanyTransferItems(crossNewItems);
    }

    /**
     * 回写报错信息
     * @param batchDumpItemId  BatchDumpItem
     * @param msg errorMsg
     */
    private void notedErrorMsg(String batchDumpItemId, String msg){
        BatchDumpItem batchDumpItem = batchDumpItemRepository.findOne(batchDumpItemId);
        batchDumpItem.setNote(msg);
        batchDumpItemRepository.saveAndFlush(batchDumpItem);
    }

    /**
     * 通过 ESB 方法调用
     * @param request BatchDump
     */
    public void approveGenerateDump(SimpleRequest request) {
        BatchDump batchDump = request.getO(BatchDump.class);

        Map<Object, List<BatchDumpItem>> item = batchDump.getBatchDumpItems().stream().collect(Collectors.groupingBy(batchDumpItem -> {
            return ((BatchDumpItem) batchDumpItem).getReceiptLocation().getId();
        }));

        // 组装调拨跨公司数据
        item.forEach((k, v) ->{
            List<BatchDumpItem> batchDumpItems = (List) v;
            int isNotCross = -1;
            if (isNotCross == -1) isNotCross = batchDump.getShipLocation().getCompany().getId().equals(batchDumpItems.get(0).getReceiptLocation().getCompany().getId())?0:1;
            if (isNotCross == 0) {
                Transfer transfer = this.appendTransfer(batchDump, batchDumpItems.get(0));
                Map transferM = MapUtil.mapped(transfer);
                Set<Map> transferItems = this.appendTransferItem(transfer, batchDumpItems);
                if (transferItems.size() > 0) {
                    transferM.put("transferItems", this.appendTransferItem(transfer, batchDumpItems));

                    Map resultEnd = new HashedMap();
                    resultEnd.put("uniqueKey_", transfer.getSource() + transfer.getReceiptLocation().getId()); // 防重用
                    resultEnd.put("transferNew", transferM);

                    // 把数据发到事务发件箱
                    this.requestOutboxAppend("batchDump-transfer-id:"+ batchDump.getId() + ","  + k, "local:batchDumpService.saveTransfer", JSONObject.toJSONString(resultEnd));
                }
            } else if (isNotCross == 1) {
                CrossCompanyTransferNew crossNew = this.appendCross(batchDump, batchDumpItems.get(0));
                Map crossNewM = MapUtil.mapped(crossNew);

                // 整理调拨范围数据
                Map transferProductAboultData =  this.qryTransferProduct(batchDump.getShipLocation(), batchDumpItems.get(0).getReceiptLocation(), batchDump.getId());
                Set<Map> crossNewItemM = this.appendCrossItem(crossNew, batchDumpItems, transferProductAboultData);
                if (crossNewItemM.size() != 0) {
                    crossNewM.put("crossCompanyTransferItems", crossNewItemM);

                    Map resultEnd = new HashedMap();
                    resultEnd.put("checkContract", transferProductAboultData.get("checkContract"));
                    resultEnd.put("flag", transferProductAboultData.get("flag"));
                    resultEnd.put("suppliers", MapUtil.mapped((List<PartyRole>) transferProductAboultData.get("suppliers")));
                    resultEnd.put("crossNew", crossNewM);
                    resultEnd.put("uniqueKey_", crossNew.getSource() + crossNew.getReceiptLocation().getId()); // 防重用

                    // 把数据发到事务发件箱
                    String key = "batchDump-cross-id:" + batchDump.getId() + "," + k; // 勿随意更改格式，有用到
                    this.requestOutboxAppend(key, "local:batchDumpService.saveCrossNew", JSONObject.toJSONString(resultEnd));
                }
            }
        });
    }

    /**
     * 查询调拨范围
     * @param shipLocation 发货仓库
     * @param receiptLocation 收货仓库
     * @return Map
     */
    public Map qryTransferProduct(Location shipLocation, Location receiptLocation, String batchDumpId) {
        String shipOperation = "", receptionOperation = "";
        try {
            shipOperation = crossCompanyTransferNewService.queryOperationModel(shipLocation.getCompany());
            receptionOperation = crossCompanyTransferNewService.queryOperationModel(receiptLocation.getCompany());
            SpringManager.getBean(StoreService.class).judgeFCKBJ(receiptLocation);
            SpringManager.getBean(StoreService.class).judgeFCKBJ(shipLocation);
        } catch (Exception e) {
//            this.updateErrorReason(e.getMessage(), "batchDump-cross-id:" + batchDumpId + "," + receiptLocation.getId());
            this.updateErrorReason(MapUtil.mapper("NOTE", "请检查是否有赋能合同！", "BATCH_DUMP_ID", batchDumpId, "RECEIPT_LOCATION_ID", receiptLocation.getId()));
            return new HashMap();
        }



        String export = "empower".equals(shipOperation) ? "001073" : "subsidiary".equals(shipOperation) ? "001661" : "001072";
        String reception = "empower".equals(receptionOperation) ? "001073" : "subsidiary".equals(receptionOperation) ? "001661" : "001072";

        List<TransferProduct> transferProductList = SpringManager.getBean(TransferProductRepository.class).findByALL(export, reception);
        boolean flag = transferProductList.stream().anyMatch(e->e.getSupplier() == null); // 所有供应商

        List<PartyRole> suppliers = new ArrayList<>();
        if (!flag){
            transferProductList = transferProductList.stream().collect(
                    collectingAndThen(toCollection(() -> new TreeSet<>(comparing(transferProduct->transferProduct.getSupplier().getId()))), ArrayList::new));
            suppliers = transferProductList.stream().map(TransferProduct::getSupplier).collect(Collectors.toList());
        }

        // 赋能--直营、子公司、合伙人，查对应合同
        boolean checkContract = false;
        if ("001073".equals(export)&& !"001073".equals(reception)) {
            checkContract = true;
            if (flag) {
                List<Contract> contracts = contractRepository.accordingCompanyValidContract("1001", "Contract.contractType.supplier");
                suppliers = contracts.stream().map(Contract::getPartner).collect(Collectors.toList());
            } else {
                List<Contract> contracts = contractRepository.accordingCompanyAndSupplierValidContract("1001", "Contract.contractType.supplier", suppliers); // 需要关注 List<PartRole> 查询条件是否生效
                suppliers = contracts.stream().map(Contract::getPartner).collect(Collectors.toList());
            }
            suppliers.addAll(partyRoleRepository.findAllInnerSupplier());
        }
        return MapUtil.mapper("flag", flag, "checkContract", checkContract, "suppliers", suppliers);
    }

    // 转储单数据拼接
    private Transfer appendTransfer(BatchDump batchDump, BatchDumpItem batchDumpItem) {
        Transfer transfer = new Transfer();
        transfer.setState("sent");
        transfer.setOriginLocation(batchDump.getShipLocation());
        transfer.setCompany(batchDump.getCompany());
        transfer.setSource(batchDump.getId());
        transfer.setSourceBusinessType(batchDump.getInnerBusinessType());
        transfer.setSourceBusinessTypeName("批量补货");
        transfer.setReceiptLocation(batchDumpItem.getReceiptLocation());
//        transfer.setNote(batchDump.getNote());
        transfer.setCreator(batchDump.getApproveUid().getId());
        transfer.setCreateUid(batchDump.getApproveUid());
        transfer.setCross(transfer.checkCross());
        transfer.setReason(enumerationRepository.findOne("000180"));
        if (transfer.checkAuto()) {
            transfer.setTransferType(enumerationRepository.findOne("000164"));
        } else {
            transfer.setTransferType(enumerationRepository.findOne("000165"));
        }
        return transfer;
    }

    // 转储明细数据拼接
    private Set<Map> appendTransferItem(Transfer transfer, List<BatchDumpItem> batchDumpItems) {
        Set<Map> transferItems = new HashSet<>();
        batchDumpItems.forEach(batchDumpItem -> {
            List<Map> stocks = this.qTransferStock(transfer.getOriginLocation().getId(), batchDumpItem.getProduct().getId());
            if (stocks.size() == 0 || Double.parseDouble(stocks.get(0).get("usableQty").toString()) < batchDumpItem.getProductSum()) {
//                this.updateErrorReason("库存不足，商品：" + batchDumpItem.getProduct().getId(), "batchDump-cross-id:" + transfer.getSource() + "," + transfer.getReceiptLocation().getId() + "," + batchDumpItem.getProduct().getId());
                this.updateErrorReason(MapUtil.mapper("NOTE", "库存不足!!", "BATCH_DUMP_ID", transfer.getSource(), "RECEIPT_LOCATION_ID", transfer.getReceiptLocation().getId(), "PRODUCT_ID", batchDumpItem.getProduct().getId()));
            } else {
                TransferItem transferItem = new TransferItem();
                transferItem.setProduct(batchDumpItem.getProduct());
                transferItem.setUom(batchDumpItem.getUom());
                transferItem.setTransferQty(batchDumpItem.getProductSum());
                transferItem.setNote(batchDumpItem.getNote());
                transferItem.setTransfer(transfer);
                transferItems.add(MapUtil.mapped(transferItem));
            }
        });
        return transferItems;
    }

    // 跨公司数据拼接
    private CrossCompanyTransferNew appendCross(BatchDump batchDump, BatchDumpItem batchDumpItem) {
        CrossCompanyTransferNew crossNew = new CrossCompanyTransferNew();
        crossNew.setSource(batchDump.getId());
        crossNew.setSourceBusinessType(batchDump.getInnerBusinessType());
        crossNew.setCompany(batchDump.getCompany());
        crossNew.setShipLocation(batchDump.getShipLocation());
        crossNew.setReceiptLocation(batchDumpItem.getReceiptLocation());
        crossNew.setReturnReason(batchDump.getReturnReason());
        crossNew.setNote(batchDump.getNote());
        crossNew.setCreateUid(batchDump.getApproveUid());
        crossNew.setState("sent");
        return crossNew;
    }

    // 跨公司明细数据拼接
    private Set<Map> appendCrossItem(CrossCompanyTransferNew crossNew, List<BatchDumpItem> batchDumpItems, /*List<PartyRole> suppliers*/Map transferProductM) {
        Set<Map> crossNewItems = new HashSet<>();
        List<PartyRole> suppliers = (List<PartyRole>) transferProductM.get("suppliers");
        boolean allSuppliers = (boolean) transferProductM.get("flag");
        if (suppliers.size() == 0 && !allSuppliers) return crossNewItems;
        batchDumpItems.forEach(batchDumpItem -> {
            // 查询无库存
            Map allStocks = new HashedMap();
            if ((boolean) transferProductM.get("flag")) {
                allStocks = queryStockRepository.availableStockAllSupplier(crossNew.getShipLocation().getId(), batchDumpItem.getProduct().getId());
            } else {
                allStocks = queryStockRepository.validStock(crossNew.getShipLocation().getId(), batchDumpItem.getProduct().getId(), suppliers);
            }
            if (allStocks.size() > 0) {
                double newUsableQty = 0;
                if (!allStocks.get("PRODUCT_UOM_ID").toString().equals(batchDumpItem.getUom().getId())) {
                    ProductUom stockUom = SpringManager.getBean(ProductUomRepository.class).findAll(JpaDsl.toCriteriaByEq("uomCategory.id", batchDumpItem.getProduct().getId(), "factor", 1)).get(0);
                    newUsableQty = Math.floor(stockUom.computeQty(Double.parseDouble(allStocks.get("USABLE_QTY").toString()), batchDumpItem.getUom(), false));
                }
                if (newUsableQty > 0 && newUsableQty >= batchDumpItem.getProductSum()) {  // 剔除无库存商品
                    CrossCompanyTransferItemNew crossNewItem = new CrossCompanyTransferItemNew();
                    crossNewItem.setCrossCompanyTransfer(crossNew);
                    crossNewItem.setProduct(batchDumpItem.getProduct());
                    crossNewItem.setUom(batchDumpItem.getUom());
                    crossNewItem.setProductSumQty(newUsableQty);
                    crossNewItem.setProductSum(batchDumpItem.getProductSum() > newUsableQty?newUsableQty:batchDumpItem.getProductSum());
                    crossNewItem.setNote(batchDumpItem.getNote());
                    crossNewItems.add(MapUtil.mapped(crossNewItem));
                } else if (newUsableQty == 0 || batchDumpItem.getProductSum() > newUsableQty) {
//                    this.updateErrorReason("库存不足，商品：" + batchDumpItem.getProduct().getId(), "batchDump-cross-id:" + crossNew.getSource() + "," + crossNew.getReceiptLocation().getId() + "," + batchDumpItem.getProduct().getId());
                    this.updateErrorReason(MapUtil.mapper("NOTE", "库存不足!!!", "BATCH_DUMP_ID", crossNew.getSource(), "RECEIPT_LOCATION_ID", crossNew.getReceiptLocation().getId(), "PRODUCT_ID", batchDumpItem.getProduct().getId()));
                }
            }
        });
        return crossNewItems;
    }

    // 请求到事务发件箱数据拼接
    private void requestOutboxAppend(String key, String subscriber, String payload) {
        OutboxPayloadWrapper outboxPayloadWrapper = new OutboxPayloadWrapper();
        outboxPayloadWrapper.setSubscriber(subscriber);
        outboxPayloadWrapper.setAggregateId(key);
        outboxPayloadWrapper.setPayload(payload);
        outboxEventService.sendSaveEvent(outboxPayloadWrapper);
    }

    @OutboxSubscriber(id = "local:batchDumpService.saveTransfer", name = "批量补货 - 转储, 同一个聚合根ID会有多条事务发件箱")
    public void saveTransfer(OutboxPayloadWrapper outboxPayloadWrapper) {
        Transfer transfer = new Transfer();
        try {
            transfer = invoke("stock.transfer.create_batchDump", outboxPayloadWrapper.getPayload());
        } catch (Exception e) {
            if (!(e.getMessage().indexOf("请不要重复提交！") > 0)) {
                JSONObject transferOutbox = JSONObject.parseObject(outboxPayloadWrapper.getPayload()).getJSONObject("transferNew");
                String receiptLocationId = transferOutbox.getJSONObject("receiptLocation").getString("id");
                this.updateErrorReason(MapUtil.mapper("NOTE", e.getMessage(), "BATCH_DUMP_ID", transferOutbox.getString("source"), "RECEIPT_LOCATION_ID", receiptLocationId, "saveAfter", true));
                throw new RuntimeException(e.getMessage());
            }
            return;
        }
        if (StringUtils.isEmpty(transfer.getId())) return;
        transfer.setApproveUid(transfer.getCreateUid());
        transfer = invoke("stock.transfer.approve", MapUtil.mapped(transfer));
        if (StringUtils.hasText(transfer.getId())) {
            this.updateBatchDumpSource(transfer.getId(), transfer.getInnerBusinessType(), transfer.getReceiptLocation().getId(), transfer.getSource());
        }
    }

    @OutboxSubscriber(id = "local:batchDumpService.saveCrossNew", name = "批量补货 - 调拨, 同一个聚合根ID会有多条事务发件箱")
    public void saveCrossNew(OutboxPayloadWrapper outboxPayloadWrapper) {
        CrossCompanyTransferNew crossNew = new CrossCompanyTransferNew();
        try {
            crossNew = invoke("cross.company.transferNew.batchDumpCrossSave", outboxPayloadWrapper.getPayload());
            if (crossNew != null && crossNew.getId() != null) {
                crossNew.setApproveUid(crossNew.getCreateUid());
                crossNew = invoke("cross.company.transferNew.approve", crossNew);
            }
        } catch (Exception e) {
            if (!(e.getMessage().indexOf("请不要重复提交！") > 0)) {
                JSONObject payload = JSONObject.parseObject(outboxPayloadWrapper.getPayload());
                JSONObject crossNewObj = payload.getJSONObject("crossNew");
                String receiptLocationId = crossNewObj.getJSONObject("receiptLocation").getString("id");
                this.updateErrorReason(MapUtil.mapper("NOTE", e.getMessage(), "BATCH_DUMP_ID", crossNewObj.getString("source"), "RECEIPT_LOCATION_ID", receiptLocationId));
                throw new RuntimeException(e.getMessage());
            }
        }
        if (StringUtils.hasText(crossNew.getId())) {
            this.updateBatchDumpSource(crossNew.getId(), crossNew.getInnerBusinessType(), crossNew.getReceiptLocation().getId(), crossNew.getSource());
        }
    }

    // 写入调拨、转储的单号
    private void updateBatchDumpSource(String dumpId, String dumpType, String receiptLocationId, String batchDumpId) {
        String tableName = dumpType.equalsIgnoreCase("transfer") ? "STOCK_TRANSFER" : "CROSS_COMPANY_TRAN_NEW";
        String tableNameItem = dumpType.equalsIgnoreCase("transfer") ? "STOCK_TRANSFER_ITEM" : "CROSS_COMPANY_TRAN_ITEM_NEW";
        String sql = "" +
                "UPDATE STOCK_BATCH_DUMP_ITEM A " +
                "   SET DOWNSTREAM_DOCUMENT = NVL2(DOWNSTREAM_DOCUMENT, DOWNSTREAM_DOCUMENT || ',' || '"+ dumpId +"', '"+dumpId+"'), " +
                "       DOWNSTREAM_DOCUMENT_TYPE = '"+ dumpType +"', NOTE = NULL " +
                " WHERE EXISTS (SELECT 1 " +
                "          FROM "+ tableName +" J, "+ tableNameItem +" K " +
                "         WHERE J.ID = K.TRANSFER_ID " +
                "           AND K.PRODUCT_ID = A.PRODUCT_ID " +
                "           AND J.ID = '"+ dumpId +"') " +
                "   AND A.RECEIPT_LOCATION_ID = '"+ receiptLocationId +"' " +
                "   AND A.BATCH_DUMP_ID = '"+ batchDumpId +"' ";
        batchDumpRepository.exec(sql);
    }

    // 转储、调拨不成功的原因
    public void updateErrorReason(Map condi) {
        SpringManager.getBean(BatchDumpTransferService.class).updateErrorReason(condi);
    }

    // 自定义导入
    public JSONObject customizeImport(SimpleRequest request) {
        JSONObject getJO = request.getJO();
        JSONObject importData = getJO.getJSONObject("importData");
        Location shipLocation = locationRepository.findOne(getJO.getJSONObject("model").getJSONObject("shipLocation").getString("id"));
        JSONArray titleColumnArray = new JSONArray();
        String key = appendImportReturn_New(importData, titleColumnArray, this.canDumpReceiptLocation(shipLocation));
        JSONObject returnData = new JSONObject();
        returnData.put(key, titleColumnArray);
        return returnData;
    }

    // 拼接导入数据
    private String appendImportReturn(JSONObject importData, JSONArray titleColumnArray, List canReceiptLocation) {
        List distinct = new ArrayList();
        Map<String, Location> cacheLocation = new HashedMap();
        Map<String, Product> cacheProduct = new HashedMap();
        Map<String, ProductUom> cacheProductUom = new HashedMap();
        AtomicReference key = new AtomicReference();
        importData.forEach((k, v) -> {
            key.set(k);
            JSONArray importDataValueArray = (JSONArray) v;
            for (int i = 0; i < importDataValueArray.size(); i++) {
                JSONArray iValueArray = (JSONArray) importDataValueArray.get(i);
                if (iValueArray.isEmpty()) continue;
                String locationId = iValueArray.get(0).toString();
                String productId = iValueArray.get(1).toString();
                String qty = iValueArray.get(2).toString();
                JSONArray newDataArray = new JSONArray();
                if (i == 0) {
                    newDataArray.add("序号");
                    newDataArray.add("收货仓库");
                    newDataArray.add("商品");
                    newDataArray.add("单位");
                    newDataArray.add("换算比率");
                    newDataArray.add("调拨数量");
                    titleColumnArray.add(newDataArray);
                    continue;
                }
                if (!canReceiptLocation.contains(locationId)) throw new RuntimeException("补货的仓库必须是父仓库同公司的正常库、物资库 和 其他公司同库区的仓库 ！");
                if (distinct.contains(locationId + productId)) throw new RuntimeException("同个仓库不能有相同商品!");
                Product product = this.cacheProduct(productId, cacheProduct);
                ProductUom uom = this.cacheProductUom(productId, cacheProductUom);
                Location location = this.cacheLocation(locationId, cacheLocation);
                newDataArray.add("");
                newDataArray.add(location);
                newDataArray.add(product);
                newDataArray.add(uom);
                newDataArray.add(uom.getFactor());
                newDataArray.add(qty);
                titleColumnArray.add(newDataArray);
                distinct.add(locationId + productId);
            }
        });
        return key.get().toString();
    }

    // 导入 - 缓存已查过的商品
    private Product cacheProduct(String productId, Map<String, Product> cacheProduct) {
        if (cacheProduct.get(productId) == null) {
            Product product = productRepository.findOne(productId);
            Optional.ofNullable(product).orElseThrow( ()-> new RuntimeException("没有找到对应商品编码的记录，商品编码为：" + productId) );
            cacheProduct.put(productId, product);
        }
        return cacheProduct.get(productId);
    }

    // 导入 - 缓存已查过的计量单位
    private ProductUom cacheProductUom(String productId, Map<String, ProductUom> cacheProductUom) {
        if (cacheProductUom.get(productId) == null) {
            ProductUom uom = productUomRepository.findAll(JpaDsl.toCriteriaByEq("uomCategory.id", productId, "transferFirst", true)).get(0);
            cacheProductUom.put(productId, uom);
        }
        return cacheProductUom.get(productId);
    }

    // 导入 - 缓存已查过的仓库
    private Location cacheLocation(String locationId, Map<String, Location> cacheLocation) {
        if (cacheLocation.get(locationId) == null) {
            Location location = locationRepository.findOne(locationId);
            Optional.ofNullable(location).orElseThrow( ()-> new RuntimeException("没有找到对应仓库编码的记录，仓库编码为：" + locationId) );
            cacheLocation.put(locationId, location);
        }
        return cacheLocation.get(locationId);
    }

    /**************************测试并行流******************************/

    private String appendImportReturn_New(JSONObject importData, JSONArray titleColumnArray, List canReceiptLocation) {
        List distinct = Collections.synchronizedList(new ArrayList());
        Map<String, Map<String, Object>> cacheLocation = new ConcurrentHashMap<>();
        Map<String, Map<String, Object>> cacheProduct = new ConcurrentHashMap();
        Map<String, Map<String, Object>> cacheProductUom = new ConcurrentHashMap();
        AtomicReference key = new AtomicReference();

        JSONArray titleArray = new JSONArray(6);
        titleArray.add("序号");
        titleArray.add("收货仓库");
        titleArray.add("商品");
        titleArray.add("单位");
        titleArray.add("换算比率");
        titleArray.add("调拨数量");
        titleColumnArray.add(titleArray);

        List dataList = Collections.synchronizedList(new ArrayList());
        importData.forEach((k, v) -> {
            key.set(k);
            JSONArray importDataValueArray = (JSONArray) v;
            importDataValueArray.parallelStream().forEach( item -> {
                JSONArray iValueArray = (JSONArray) item;
                if (iValueArray.size() == 0) return;
                this.judgeDataError(iValueArray);
                String locationId = iValueArray.get(0).toString();
                String productId = iValueArray.get(1).toString();
                String qty = iValueArray.get(2).toString();
                JSONArray newDataArray = new JSONArray();
                if (Pattern.matches("[\\u4e00-\\u9fa5]*", locationId)) {    // 匹配中文
                    return;
                }
                if (!canReceiptLocation.contains(locationId)) throw new RuntimeException("补货的仓库必须是父仓库同公司的正常库、物资库 和 其他公司同库区的仓库 ！");
                if (distinct.contains(locationId + productId)) throw new RuntimeException("同个仓库不能有相同商品!");

                Map<String, Object> product = this.cacheProduct_new(productId, cacheProduct);
                Map<String, Object> uom = this.cacheProductUom_new(productId, cacheProductUom);
                Map<String, Object> location = this.cacheLocation_new(locationId, cacheLocation);
                newDataArray.add("");
                newDataArray.add(location);
                newDataArray.add(product);
                newDataArray.add(uom);
                newDataArray.add(Double.parseDouble(uom.get("factor").toString()));
                newDataArray.add(qty);
                dataList.add(MapUtil.mapped(newDataArray));
                distinct.add(locationId + productId);
            });
        });

        titleColumnArray.addAll(dataList);
        return key.get().toString();
    }

    // 数据准确性判断
    private void judgeDataError(JSONArray iValueArray) {
        if (StringUtils.isEmpty(iValueArray.get(0))) throw new RuntimeException("存在非法的仓库编码，请检查数据！");
        if (StringUtils.isEmpty(iValueArray.get(1))) throw new RuntimeException("存在非法的商品编码，请检查数据！");
        if (StringUtils.isEmpty(iValueArray.get(2))) throw new RuntimeException("存在非法的数量，请检查数据！");
    }

    // 导入 - 缓存已查过的商品
    private Map<String, Object> cacheProduct_new(String productId, Map<String, Map<String, Object>> cacheProduct) {
        if (cacheProduct.get(productId) == null) {
            Product product = productRepository.findOne(productId);
            Optional.ofNullable(product).orElseThrow( ()-> new RuntimeException("没有找到对应商品编码的记录，商品编码为：" + productId) );
            cacheProduct.put(productId, MapUtil.mapper("id", product.getId(), "name", product.getName()));
        }
        return cacheProduct.get(productId);
    }

    // 导入 - 缓存已查过的计量单位
    private Map<String, Object> cacheProductUom_new(String productId, Map<String, Map<String, Object>> cacheProductUom) {
        if (cacheProductUom.get(productId) == null) {
            ProductUom uom = productUomRepository.findAll(JpaDsl.toCriteriaByEq("uomCategory.id", productId, "transferFirst", true)).get(0);
            cacheProductUom.put(productId, MapUtil.mapper("id", uom.getId(), "name", uom.getName(), "factor", uom.getFactor()));
        }
        return cacheProductUom.get(productId);
    }

    // 导入 - 缓存已查过的仓库
    private Map<String, Object> cacheLocation_new(String locationId, Map<String, Map<String, Object>> cacheLocation) {
        if (cacheLocation.get(locationId) == null) {
            Location location = locationRepository.findOne(locationId);
            Optional.ofNullable(location).orElseThrow( ()-> new RuntimeException("没有找到对应仓库编码的记录，仓库编码为：" + locationId) );
            cacheLocation.put(locationId, MapUtil.mapper("id", location.getId(), "name", location.getName()));
        }
        return cacheLocation.get(locationId);
    }

    /**************************测试并行流******************************/

    // 获取能够补货的收货仓
    private List canDumpReceiptLocation(Location shipLocation) {
        String shipCompanyId = shipLocation.getCompany().getId();
        return locationRepository.findAll("SELECT ID FROM MAS_LOCATION WHERE " +
                "((COMPANY_ID = '"+ shipCompanyId +"' AND USAGE_ID IN ('1', '4')) OR (COMPANY_ID != '"+ shipCompanyId +"' AND USAGE_ID = '"+ shipLocation.getUsage().getId() +"')) " +
                "AND ID != '"+ shipLocation.getId() +"'");
    }

    // 审核执行 - 转储获取库存判断
    private List<Map> qTransferStock(String locationId, String productId) {
        Map condiAll = new HashedMap();
        List<Filter> conditions = new ArrayList<>();
        Filter filter = new Filter();
        filter.setField("product.id");
        filter.setOperator("EQ");
        filter.setValue(productId);
        conditions.add(filter);
        condiAll.put("condition", conditions);

        Map filterM = new HashedMap();
        List<Filter> filters = new ArrayList<>();
        Filter filter2 = new Filter();
        filter2.setField("location.id");
        filter2.setOperator("EQ");
        filter2.setValue(locationId);
        conditions.add(filter2);
        filters.add(filter2);
        filterM.put("filters", filters);
        filterM.put("groups", new HashMap<>());
        filterM.put("operator", "and");
        condiAll.put("domain", filterM);
        condiAll.put("size", 20);
        condiAll.put("page", 0);

        SimpleRequest request = new SimpleRequest();
        request.setBody(JSON.toJSONString(condiAll));
        Page<Map> stocks = SpringManager.getBean(ProductAccountService.class).findAllUsableProduct(request);
        if (stocks != null) return stocks.getContent();
        return new ArrayList<>();
    }

    // 获取收货仓库
    public Page<Location> queryReceiptLocation(SimpleRequest request) {
        JSONObject getJO = request.getJO();
        JSONObject groupParamObj = getJO.getJSONObject("group");
        JSONObject domainParamObj = getJO.getJSONObject("domain");
        JSONArray filterArray = groupParamObj != null?groupParamObj.getJSONArray("filters"):domainParamObj.getJSONArray("filters");
        JSONArray inputKeyArray = groupParamObj != null?groupParamObj.getJSONArray("groups").getJSONObject(0).getJSONArray("filters"):getJO.getJSONArray("condition");

        Group group = this.appendCondition(filterArray, inputKeyArray);
        return SpringManager.getBean(LocationRepository.class).findAll(group.build(false), JpaDsl.getPageable(request));
    }

    // 拼接查询条件
    private Group appendCondition(JSONArray filterArray, JSONArray inputKeyArray) {
        Group group = new Group();
        List<Filter> filters = new ArrayList<>();
        AtomicReference shipCompanyId = new AtomicReference();
        filterArray.forEach(obj -> {
            Filter filter = new Filter();
            JSONObject filterObj = (JSONObject) obj;
            if ("shipLocationId".equals(filterObj.getString("field"))) {
                filter.setField("id");
                filter.setOperator("NE");
                filter.setValue(filterObj.getString("value"));
                filters.add(filter);
            } else if ("shipCompanyId".equals(filterObj.getString("field"))) {
                shipCompanyId.set(filterObj.getString("value"));
            }
        });
        group.addFilters(filters); // 第一层：排除父仓库

        boolean keyFlag = true;
        List<Group> groupSeconds = new ArrayList<>();
        Group groupSecond = new Group();
        List<Filter> filterSeconds = new ArrayList<>();
        if (inputKeyArray != null && inputKeyArray.size() != 0 && !StringUtils.isEmpty(inputKeyArray.getJSONObject(0).getString("value"))) {
            keyFlag = false;
            JSONObject filtersSecondObj = inputKeyArray.getJSONObject(0);
            Filter filterKey = new Filter();
            filterKey.setField(filtersSecondObj.getString("field"));
            filterKey.setOperator(filtersSecondObj.getString("operator"));
            filterKey.setValue(filtersSecondObj.getString("value"));
            filterSeconds.add(filterKey);

            filterKey = new Filter();
            filterKey.setField("name");
            filterKey.setOperator("LIKE");
            filterKey.setValue(filtersSecondObj.getString("value"));
            filterSeconds.add(filterKey);


            groupSecond.setFilters(filterSeconds);
            groupSecond.setOperator("or");
            groupSeconds.add(groupSecond); // 第二层：获取输入的 key

            // 添加库区类型
            Group usageGroup = new Group(); // 添加仓库库区
            List<Filter> usageFilters = new ArrayList<>();
            Filter usageFilter = new Filter();
            usageFilter.setField("usage.id");
            usageFilter.setValue("1");
            usageFilter.setOperator("EQ");
            usageFilters.add(usageFilter);
            usageGroup.setFilters(usageFilters);
            groupSeconds.add(usageGroup);
            group.setGroups(groupSeconds);
        }


        //--------------------------以下是无 key 值的情况------------------------------------------------------
        if (keyFlag) {
            List<Group> mainThrees = new ArrayList<>();
            Group mainData = new Group();
            mainData.setOperator("or");
            List<Group> groupThrees = new ArrayList<>();

            Group groupThree = new Group(); // 第三层：父仓库同公司的其他库区
            List<Filter> filterThrees = new ArrayList<>();
            Filter filter = new Filter();
            filter.setField("company.id");
            filter.setValue(shipCompanyId.get());
            filter.setOperator("EQ");
            filterThrees.add(filter);

            filter = new Filter();
            filter.setField("usage.id");
            filter.setValue(Arrays.asList("1", "4"));
            filter.setOperator("in");
            filterThrees.add(filter);
            groupThree.setFilters(filterThrees);
            groupThree.setOperator("AND");
            groupThrees.add(groupThree);
            groupSeconds.add(groupThree);

            Group groupFour = new Group(); // 第三层：其他公司的正常库 |（物资库）
            List<Filter> filterFours = new ArrayList<>();
            Filter filterFour = new Filter();
            filterFour.setField("company.id");
            filterFour.setValue(shipCompanyId.get());
            filterFour.setOperator("NE");
            filterFours.add(filterFour);

            filterFour = new Filter();
            filterFour.setField("usage.id");
            filterFour.setValue("1");
            filterFour.setOperator("EQ");
            filterFours.add(filterFour);
            groupFour.setFilters(filterFours);
            groupFour.setOperator("AND");
            groupSeconds.add(groupFour);
            mainData.setGroups(groupSeconds);
            mainThrees.add(mainData);
            group.setGroups(mainThrees);
        }
        return group;
    }

    // 同一个订阅编码多个包含多条【调拨】的消息，存在事务堵塞，使用定时任务实时触发
    public synchronized void timingConsumBatchDumpCross() {
        String aggregateId = "crossDumpTransfer:";
        String subscriberId = "local:batchDumpService.saveCrossNew";
        Map crossDumpOutbox = SpringManager.getBean(OutboxEventRepository.class).accordAggregateIdQ(aggregateId, subscriberId);
        if (crossDumpOutbox.isEmpty()) return;
        invoke("outbox.message.accordAggregateBbatchProcessing", MapUtil.mapper("aggregateId", aggregateId, "outboxSubscriberId", subscriberId));
    }

    // 同一个订阅编码多个包含多条【转储】的消息，存在事务堵塞，使用定时任务实时触发
    public synchronized void timingConsumBatchDumpTransfer() {
        String aggregateId = "dumpTransfer:";
        String subscriberId = "local:batchDumpService.saveTransfer";
        Map crossDumpOutbox = SpringManager.getBean(OutboxEventRepository.class).accordAggregateIdQ(aggregateId, subscriberId);
        if (crossDumpOutbox.isEmpty()) return;

        invoke("outbox.message.accordAggregateBbatchProcessing", MapUtil.mapper("aggregateId", aggregateId, "outboxSubscriberId", subscriberId));

    }
}
